package cn.kwq.pcsystem.blockChain;

import cn.hutool.crypto.digest.DigestUtil;
import cn.kwq.pcsystem.exception.BlockChainError;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Query;

import java.util.Optional;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: kwq
 * @Date: 2023/03/19/16:47
 * @Description:
 */
@Slf4j
@Data
public class BlockChainUtils {
    /**
     * 难度级别，用于控制区块链插入的速度
     */
    private static final int DIFFICULT=1;

    @Autowired
    private MongoTemplate mongoTemplate;

    public static BlockChainUtils getChain(){
        return new BlockChainUtils();
    }

    private void addBlockToChain(String data){
            //链为空时
            if (getLastBlock()==null){
                Block genesisBlock = getGenesisBlock();
                add(genesisBlock);
            }
            Block block=new Block();
            //检验区块链可信度
            if (!validateChain()){
                throw new BlockChainError("区块链不可信！");
            }

            //获取区块链上的Hash作为pre
            String preHash = getLastBlock().getHash();
            block.setPreHash(preHash);
            block.setData(data);
            block.setHash(computerHash(block));
            add(block);
    }

    private Block getLastBlock(){
        Query query = new Query();
        query.with(Sort.by(Sort.Order.desc("_id"))).limit(1);
        return mongoTemplate.findOne(query, Block.class);
    }

    private String computerHash(Block block){
        String hash;
        //设置难度(Proof of Work) 穷举
        while(true){
            //生成hash前difficult位都是0的情况
            if (getDifficultStr().equals(simpleHash(block).substring(0,DIFFICULT))){
                hash=simpleHash(block);
                break;
            }else {
                block.setNonce(block.getNonce()+1);
            }
        }
        return hash;
    }

    //检查区块链是否被修改
    private boolean validateChain(){
        boolean b=true;
        long total = mongoTemplate.getCollection("PCS_record_blockChain").estimatedDocumentCount();
        if (total==1L){
            return true;
        }
        //第二个区块开始验证
        for (long i=1;i<total;i++){
            Block block = get(i);//当前验证的区块
            //验证数据是否被串改
            if (!block.getHash().equals(simpleHash(block))){
                b=false;
                log.error("区块链数据已被篡改！！！在:{}处",i);
                break;
            }
            //验证preHash是不是等于上个hash，链条完整性
            Block preBlock = get(i - 1);
            if (!block.getPreHash().equals(preBlock.getHash())){
                b=false;
                log.error("区块链已断开！！！在:{}处",i);
                break;
            }
        }
        return b;
    }

    /**
     * 简单生成hash
     * @param block
     * @return
     */
    private  String simpleHash(Block block){
        return DigestUtil.sha256Hex(block.getData()+block.getPreHash()+block.getNonce());
    }

    /**
     * 生成验证前缀字符串算法
     * @return
     */
    private String getDifficultStr(){
        return "0".repeat(DIFFICULT + 1);
    }

    /**
     * 生成祖先区块
     * @return
     */
    private Block getGenesisBlock(){
        Block genesisBlock= new Block();
        genesisBlock.setPreHash("");
        genesisBlock.setData("祖先区块");
        genesisBlock.setHash(simpleHash(genesisBlock));
        return genesisBlock;
    }
    private Block get(long i){
        Query query = new Query();
        query.with(Sort.by(Sort.Order.asc("_id"))).limit(1).skip(i);
        return mongoTemplate.findOne(query, Block.class);
    }
    private void add(Block b){
        mongoTemplate.insert(b);
    }
}




